package xyz.xtt.asset.service.impl;

import static java.util.stream.Collectors.toList;
import static xyz.xtt.common.asset.AssertUtils.isNull;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.Objects;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.primitives.Longs;

import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import xyz.xtt.asset.client.request.CardInfoRequest;
import xyz.xtt.asset.constants.AssetCardDestoryStatenum;
import xyz.xtt.asset.constants.AssetCardRceiveStatenum;
import xyz.xtt.asset.constants.ErrorMessage;
import xyz.xtt.asset.entity.TblAsset;
import xyz.xtt.asset.entity.TblAssetCard;
import xyz.xtt.asset.entity.TblAssetExt;
import xyz.xtt.asset.entity.TblAssetNo;
import xyz.xtt.asset.entity.TblAssetStaticResource;
import xyz.xtt.asset.enums.CardDestoryStatus;
import xyz.xtt.asset.exception.ChainException;
import xyz.xtt.asset.mapper.TblAssetCardMapper;
import xyz.xtt.asset.service.IInnerService;
import xyz.xtt.asset.service.ITblAssetCardService;
import xyz.xtt.asset.service.ITblAssetExtService;
import xyz.xtt.asset.service.ITblAssetService;
import xyz.xtt.asset.service.ITblAssetStaticResourceService;
import xyz.xtt.asset.vo.AssetCardInfo;
import xyz.xtt.common.asset.AssertUtils;
import xyz.xtt.pay.client.response.BusinessOrderResponse;
import xyz.xtt.pay.client.response.TblPayOrderDto;

/**
 * <p>
 * 资产实体卡表 服务实现类
 * </p>
 *
 * @author author
 * @since 2025-02-25
 */
@RefreshScope
@Service
@Slf4j
public class TblAssetCardServiceImpl extends ServiceImpl<TblAssetCardMapper, TblAssetCard> implements ITblAssetCardService {
	@Autowired
	private ITblAssetService tblAssetService;
	@Autowired
	private ITblAssetExtService tblAssetExtService;
	@Autowired
	private ITblAssetStaticResourceService tblAssetStaticResourceService;
	@Autowired
	private TblAssetCardMapper tblAssetCardMapper;
	@Autowired
	private IInnerService iInnerService;
	@Value("${asset.cardno.start:100000}")
	private Long cardNoStart;

	@Override
	public AssetCardInfo getAssetByCardId(Long id) {
		TblAssetCard tblAssetCard = baseMapper.selectById(id);
		if (Objects.isNull(tblAssetCard)) {
			throw new ChainException(ErrorMessage.CODE_ERROR);
		}
		if (tblAssetCard.getDestoryState().equals(CardDestoryStatus.LOCK.getCode())) {
			throw new ChainException(ErrorMessage.CODE_ERROR);
		}
		// 查询资产信息
//        TblAsset tblAsset = tblAssetService.s(tblAssetCard.getAssetId());
		TblAsset tblAsset = tblAssetService.getById(tblAssetCard.getAssetId());
		if (Objects.isNull(tblAsset)) {
			throw new ChainException(ErrorMessage.CODE_ERROR);
		}
		TblAssetExt tblAssetExt = tblAssetExtService.infoWithAssetIdByCache(tblAsset.getId());
		TblAssetStaticResource tblAssetStaticResource = tblAssetStaticResourceService.infoWithAssetIdByCache(tblAsset.getId());

		return AssetCardInfo
				.builder()
				.assetName(tblAsset.getAssetName())
				.imgUrl(tblAssetStaticResource.getLogoUri())
				.tokenId(tblAssetExt.getTokenId())
				.chainName(tblAssetExt.getChainName())
				.referProtocol(tblAssetExt.getReferProtocol())
				.chainTxHash(tblAssetExt.getChainTxHash())
				.chainBlockNum(tblAssetExt.getChainBlockNum())
				.chainTime(tblAssetExt.getChainTime())
				.build();
	}

	@Override
	public void updateAssetCardReceiveState(TblPayOrderDto payOrderDto) {
		BusinessOrderResponse businessOrder = iInnerService.businessOrderInfoWithId(Longs.tryParse(payOrderDto.getBusinessRefId()));
		isNull(businessOrder, ErrorMessage.BUSINESS_ORDER_NOT_EXISTS,()->log.info("param:{}", JSONUtil.toJsonStr(payOrderDto)));
		LambdaUpdateWrapper<TblAssetCard> updateWrapper = new LambdaUpdateWrapper<>();
		updateWrapper.eq(TblAssetCard::getId, Long.valueOf(businessOrder.getGoodsId()));
		updateWrapper.set(TblAssetCard::getReceiveState, 1);
		int update = tblAssetCardMapper.update(updateWrapper);
		log.info("updateAssetCardReceiveState:{},payOrderDto:{}", update, payOrderDto);
	}

	@Override
	public boolean destoryCard(Long assetId, Long userId) {
		LambdaUpdateChainWrapper<TblAssetCard> update = new LambdaUpdateChainWrapper<>(baseMapper);
		return update
				.eq(TblAssetCard::getAssetId, assetId)
				.eq(TblAssetCard::getOwnerId, userId)
				.ne(TblAssetCard::getDestoryState, AssetCardDestoryStatenum.DRSTORY.code())
				.set(TblAssetCard::getUpdateTime, System.currentTimeMillis())
				.set(TblAssetCard::getDestoryState, AssetCardDestoryStatenum.DRSTORY.code())
				.update();
	}

	@Override
	public boolean destoryCard(Long assetId, Long userId, int n) {
		List<Long> collect = new LambdaQueryChainWrapper<>(baseMapper)
				.eq(TblAssetCard::getAssetId, assetId)
				.eq(TblAssetCard::getOwnerId, userId)
				.ne(TblAssetCard::getDestoryState, AssetCardDestoryStatenum.DRSTORY.code())
				.list()
				.stream()
				.map(TblAssetCard::getId)
				.collect(toList());
		AssertUtils.isTrue(collect.size() < n, ErrorMessage.DESTORY_OPERATOR_FAIL);
		List<Long> ids = collect.stream().limit(n).collect(toList());
		LambdaUpdateChainWrapper<TblAssetCard> update = new LambdaUpdateChainWrapper<>(baseMapper);
		return update
				.in(TblAssetCard::getId, ids)
				.set(TblAssetCard::getDestoryState, AssetCardDestoryStatenum.DRSTORY.code())
				.set(TblAssetCard::getUpdateTime, LocalDateTime.now())
				.update();
	}

	/**
	 * 作废同一资产编号下的卡牌
	 *
	 * @param assetNoId
	 */
	@Override
	public void updateUserRepeatCardInvalid(long assetNoId) {
		LambdaUpdateChainWrapper<TblAssetCard> update = new LambdaUpdateChainWrapper<>(baseMapper);
		update.eq(TblAssetCard::getAssetNoId, assetNoId).set(TblAssetCard::getDestoryState, 1).update();
	}

	/**
	 * 为卡牌申领人添加新卡牌
	 *
	 * @param tblAssetNo
	 */
	@Override
	public boolean insertAssetCard(TblAssetNo tblAssetNo) {
		LocalDateTime now = LocalDateTime.now();
		TblAssetCard assetCard = TblAssetCard
				.builder()
				.id(IdUtil.getSnowflakeNextId())
				.assetId(tblAssetNo.getAssetId())
				.ownerId(tblAssetNo.getAppUserId())
				.cardNo(IdUtil.getSnowflakeNextId())
				.destoryState(AssetCardDestoryStatenum.NORMAL.code())
				.receiveState(AssetCardRceiveStatenum.RECEIVE.code())
				.createTime(now)
				.receiveTime(now.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli())
				.updateTime(now)
				.assetNo(tblAssetNo.getAssetNo())
				.assetNoId(tblAssetNo.getId())
				.build();
		return this.save(assetCard);
	}

	/**
	 * 批量作废资产编号下的卡牌
	 *
	 * @param assetNoIds
	 */
	@Override
	public void updateInvalidByAssetNos(List<Long> assetNoIds) {
		LambdaUpdateChainWrapper<TblAssetCard> update = new LambdaUpdateChainWrapper<>(baseMapper);
		update.in(TblAssetCard::getAssetNoId, assetNoIds).set(TblAssetCard::getDestoryState, 1).update();
	}

	@Override
	public boolean genCard(Long assetId, Long userId) {
		LocalDateTime now = LocalDateTime.now();
		TblAssetCard build = TblAssetCard
				.builder()
				.id(IdUtil.getSnowflakeNextId())
				.assetId(assetId)
				.ownerId(userId)
				.cardNo(IdUtil.getSnowflakeNextId())
				.destoryState(AssetCardDestoryStatenum.NORMAL.code())
				.receiveState(AssetCardRceiveStatenum.DEFAULT.code())
				.createTime(now)
				.updateTime(now)
				.build();
		return this.save(build);
	}

	@Override
	public List<TblAssetCard> getUserCardInfo(CardInfoRequest cardInfoRequest) {
		LambdaQueryWrapper<TblAssetCard> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(TblAssetCard::getOwnerId, cardInfoRequest.getOwnerId());
		if (cardInfoRequest.getAssetId() != null) {
			queryWrapper.eq(TblAssetCard::getAssetId, cardInfoRequest.getAssetId());
		}
		if (Objects.nonNull(cardInfoRequest.getCardNos()) && !CollectionUtils.isEmpty(cardInfoRequest.getCardNos())) {
			queryWrapper.in(TblAssetCard::getCardNo, cardInfoRequest.getCardNos());
		}
		if (Objects.nonNull(cardInfoRequest.getAssetNos()) && !CollectionUtils.isEmpty(cardInfoRequest.getAssetNos())) {
			queryWrapper.in(TblAssetCard::getAssetNo, cardInfoRequest.getAssetNos());
		}
		queryWrapper.eq(TblAssetCard::getDestoryState, 0);
		queryWrapper.orderByDesc(TblAssetCard::getCreateTime);
		return baseMapper.selectList(queryWrapper);
	}
}
